<template>
  <g
    :class="animateClass"
    :transform="`translate(${dragX},${dragY}) rotate(${rotate},${centerPoint.x},${centerPoint.y})`"
    :data-x="dragX"
    :data-y="dragY"
    @mousedown="dragStart"
    @contextmenu="contextmenu($event, $el)"
  >
    <rect
      v-if="!hideBg"
      :x="selectX"
      :y="selectY"
      rx="16"
      ry="16"
      :width="selectWidth"
      :height="selectHeight"
      fill="#F3F5F7"
      stroke="#F3F5F7"
      :style="{ 'stroke-width': 1 }"
      pointer-events="none"
    />
    <EditableText
      v-if="!hideLabel"
      v-model:value="labelText"
      v-model:width="labelWidth"
      v-model:height="labelHeight"
      :readonly-text-class="'vc-select-none ' + (isEditor ? 'vc-cursor-move' : 'vc-cursor-pointer')"
      :text-css-props="labelCssProps"
      :stop-propagation="false"
      :x="labelX"
      :y="labelY"
      :readonly="!isEditor"
      @blur="labelBlur"
    />
    <rect v-if="!hideValue && !hideLabel" v-bind="splitProps" fill="rgba(0,0,0,0)" />
    <EditableText
      v-if="!hideValue"
      v-model:value="valueText"
      v-model:width="valueWidth"
      v-model:height="valueHeight"
      :readonly-text-class="'vc-select-none ' + (isEditor ? 'vc-cursor-move' : 'vc-cursor-pointer')"
      :text-css-props="valueCssProps"
      :stop-propagation="false"
      :x="valueX"
      :y="valueY"
      :readonly="true"
      @blur="valueBlur"
    />
    <EditableText
      v-if="!hideValue && !hideUnit"
      v-model:value="unitText"
      v-model:width="unitWidth"
      v-model:height="unitHeight"
      :readonly-text-class="'vc-select-none ' + (isEditor ? 'vc-cursor-move' : 'vc-cursor-pointer')"
      :text-css-props="unitCssProps"
      :stop-propagation="false"
      :x="valueX + ValueToUnit + valueWidth"
      :y="valueY + (valueHeight - unitHeight) / 2"
      @blur="unitBlur"
    />
    <template v-if="selected">
      <rect
        :x="selectX"
        :y="selectY"
        :width="selectWidth"
        :height="selectHeight"
        fill="none"
        stroke="#4F80FF"
        stroke-dasharray="3 3"
        :style="{ 'stroke-width': isEditor ? 1 : 2 }"
        pointer-events="none"
      />
      <ConfigureIcon
        v-if="isEditor"
        :x="selectX + selectWidth / 2 - IconSize / 2"
        :y="labelY - SelectToContent - IconSize"
        :size="IconSize"
        @mousedown="rotateStart"
      />
    </template>
  </g>
</template>
<script setup lang="ts">
import { computed, CSSProperties, inject, nextTick, onBeforeUnmount, onMounted, ref, watch } from "vue";
import { CompCommon, CompCommonKey, ConfigurationInstance, LabelValueJSON, LabelValuePart } from "/@/components/types";

import EditableText from "/@/components/base/EditableText.vue";
import { ValueColor, IconSize, UnitColor, getApplyCondition, LabelColor } from "/@/components/common";
import { useDrag } from "/@/hooks/useDrag";
import { useRotate } from "/@/hooks/useRotate";
import { isRectIntersect } from "/@/utils/draw";
import ConfigureIcon from "/@/components/base/ConfigureIcon.vue";
import { TextFontSzie } from "/@/components/common";
import { ContextmenuItem, SvgInfo, ConfigurationType } from "/@/Configuration/types";
import { LabelValuePanelInfo } from "/@/Configuration/AttrPanel/types";
import { AnimateClass, Condition, LabelValueOriginalProps } from "/@/Configuration/ConditionPanel/types";

//label与value的间距
const LabelToValue = 5;
//选择边框与内容的间距
const SelectToContent = 5;
//值与单位间距
const ValueToUnit = 3;

const props = withDefaults(
  defineProps<{
    id: string;
    centerX?: number;
    centerY?: number;
    labelPlaceholder?: string;
    valuePlaceholder?: string;
    initLabelX?: number;
    initLabelY?: number;
    index: number;
    scaleFactor: number;
  }>(),
  {
    labelPlaceholder: "label",
    valuePlaceholder: "value",
  }
);
const position = ref<"vertical" | "horizontal">("vertical");
const compCommon = inject(CompCommonKey) as CompCommon<LabelValuePanelInfo>;
const isEditor = compCommon.isEditor;
const svgInfo = compCommon.getSvgInfo() as SvgInfo;
function getLabelCssProps(newProps?: CSSProperties) {
  return Object.assign({ "font-size": TextFontSzie * props.scaleFactor + "px", color: LabelColor }, newProps);
}
function getValueCssProps(newProps?: CSSProperties) {
  return Object.assign({ "font-size": TextFontSzie * props.scaleFactor + "px", color: ValueColor }, newProps);
}
function getUnitCssProps(newProps?: CSSProperties) {
  return Object.assign({ "font-size": TextFontSzie * props.scaleFactor + "px", color: UnitColor }, newProps);
}
const labelCssProps = ref<CSSProperties>(getLabelCssProps());
const valueCssProps = ref<CSSProperties>(getValueCssProps());
const unitCssProps = ref<CSSProperties>(getUnitCssProps());

const dragX = ref(0);
const dragY = ref(0);

const labelX = ref(0);
const labelY = ref(0);
const labelWidth = ref(0);
const labelHeight = ref(0);
const labelText = ref(props.labelPlaceholder);
function labelBlur() {
  if (!labelText.value) {
    labelText.value = props.labelPlaceholder;
  }
}
//缩放前的值
let _labelX = 0;
let _labelY = 0;
let _labelFontSize = TextFontSzie;
let _valueFontSize = TextFontSzie;
let _unitFontSize = TextFontSzie;
watch([labelX, labelY], ([v1, v2]) => {
  _labelX = v1 / props.scaleFactor;
  _labelY = v2 / props.scaleFactor;
});
//value
const valueWidth = ref(0);
const valueHeight = ref(0);
const valueX = computed(() => {
  if (position.value === "vertical") {
    const unitSpace = hideUnit.value ? 0 : ValueToUnit + unitWidth.value;
    return labelX.value + (labelWidth.value - (valueWidth.value + unitSpace)) / 2;
  } else {
    return labelX.value + labelWidth.value + getLabelToValue();
  }
});
const valueY = computed(() => {
  if (position.value === "vertical") {
    return labelY.value + labelHeight.value + getLabelToValue();
  } else {
    return labelY.value;
  }
});
const valueText = ref(props.valuePlaceholder);
function valueBlur() {
  if (!valueText.value) {
    valueText.value = props.valuePlaceholder;
  }
}
//unit
const unitWidth = ref(0);
const unitHeight = ref(0);
const unitText = ref("unit");
function unitBlur() {
  if (!unitText.value) {
    unitText.value = "unit";
  }
}

onMounted(() => {
  if (props.centerX !== undefined && props.centerY !== undefined) {
    if (props.centerX === 0 && props.centerY === 0) return;
    //手动拖拽生成的数据
    labelX.value = props.centerX - labelWidth.value / 2;
    labelY.value = props.centerY - labelHeight.value / 2;
  }
});
/**
 * select
 */
//传过去面板信息的fontSize应该缩放前的fontSize
function dealScaleCssProps(cssProps: CSSProperties, labelValuePart: LabelValuePart) {
  const copy = { ...cssProps };
  let fSize: number | null = null;
  switch (labelValuePart) {
    case LabelValuePart.Label:
      fSize = _labelFontSize;
      break;
    case LabelValuePart.Value:
      fSize = _valueFontSize;
      break;
    case LabelValuePart.Unit:
      fSize = _unitFontSize;
      break;
  }
  if (fSize) {
    copy["font-size"] = fSize;
  }
  return copy;
}
function select(multiple?: boolean) {
  selected.value = true;
  compCommon.toggleSelect({
    type: ConfigurationType.LabelValue,
    selected: true,
    multiple,
    index: props.index,
    unSelect() {
      selected.value = false;
    },
    id: props.id,
    drag: {
      move,
      end,
    },
    getLabelValueInfo() {
      return {
        labelCssProps: dealScaleCssProps(labelCssProps.value, LabelValuePart.Label),
        valueCssProps: dealScaleCssProps(valueCssProps.value, LabelValuePart.Value),
        unitCssProps: dealScaleCssProps(unitCssProps.value, LabelValuePart.Unit),
        rotate: rotate.value,
        position: position.value,
        hideValue: hideValue.value,
        hideUnit: hideUnit.value,
        hideLabel: hideLabel.value,
        dataKey: dataKey.value,
        setDataKey(val) {
          dataKey.value = val;
        },
        setRotate(val) {
          rotate.value = val;
        },
        setCssProps(cssProps, labelValuePart) {
          if (cssProps["font-size"]) {
            const newFontSize = parseInt(cssProps["font-size"] as string);
            cssProps["font-size"] = newFontSize * props.scaleFactor + "px";
            switch (labelValuePart) {
              case LabelValuePart.Label:
                _labelFontSize = newFontSize;
                labelCssProps.value = cssProps;
                break;
              case LabelValuePart.Value:
                _valueFontSize = newFontSize;
                valueCssProps.value = cssProps;
                break;
              case LabelValuePart.Unit:
                _unitFontSize = newFontSize;
                unitCssProps.value = cssProps;
                break;
            }
          }
        },
        setPosition(val) {
          position.value = val;
        },
        setPartHide(isHide, labelValuePart) {
          switch (labelValuePart) {
            case LabelValuePart.Label:
              hideLabel.value = isHide;
              break;
            case LabelValuePart.Value:
              hideValue.value = isHide;
              break;
            case LabelValuePart.Unit:
              hideUnit.value = isHide;
              break;
          }
        },
      };
    },
  });
}
/**
 * drag
 */
function move({ dx, dy }: { dx?: number; dy?: number }) {
  dragX.value = dx as number;
  dragY.value = dy as number;
}
function end() {
  labelX.value += dragX.value;
  labelY.value += dragY.value;
  dragX.value = 0;
  dragY.value = 0;
}

const { dragStart } = useDrag<[], { isDragSelect: boolean }>({
  unStopPropagation: !isEditor.value,
  element: svgInfo.container,
  start() {
    const isDragSelect = compCommon.multipleSelect.startTogether(props.id);
    if (!isDragSelect) {
      select();
    }
    return { isDragSelect };
  },
  move({ dx, dy, startResult }) {
    if (!isEditor.value) return;
    if (startResult.isDragSelect) {
      compCommon.multipleSelect.moveTogether({ dx, dy });
    } else {
      move({ dx, dy });
    }
  },
  end({ isClick, startResult }) {
    if (startResult.isDragSelect) {
      compCommon.multipleSelect.endTogether();
    } else {
      end();
    }
    if (isClick) {
      select();
    }
  },
});
/**
 * selected
 */
function getLabelToValue() {
  return hideLabel.value || hideValue.value ? 0 : LabelToValue;
}
const selected = ref(false);
const selectX = computed(() => {
  if (position.value === "vertical" && !hideValue.value) {
    return Math.min(labelX.value, valueX.value) - SelectToContent;
  } else {
    return labelX.value - SelectToContent;
  }
});
const selectY = computed(() => {
  return labelY.value - SelectToContent;
});
const selectWidth = computed(() => {
  const unitSpace = hideUnit.value ? 0 : ValueToUnit + unitWidth.value;
  const valueUnitWidth = hideValue.value ? 0 : valueWidth.value + unitSpace;
  if (position.value === "vertical") {
    return Math.max(labelWidth.value, valueUnitWidth) + SelectToContent * 2;
  } else {
    return labelWidth.value + getLabelToValue() + valueUnitWidth + SelectToContent * 2;
  }
});
const selectHeight = computed(() => {
  if (position.value === "vertical") {
    return labelHeight.value + (hideValue.value ? 0 : valueHeight.value) + getLabelToValue() + SelectToContent * 2;
  } else {
    return Math.max(labelHeight.value, hideValue.value ? 0 : valueHeight.value) + SelectToContent * 2;
  }
});
/**
 * hide
 */
const hideValue = ref(false);
const hideLabel = ref(false);
const hideBg = ref(false);
let isTriggerHideLabelWatch = true;
watch(hideLabel, (v) => {
  if (v && isTriggerHideLabelWatch) {
    nextTick(() => {
      labelX.value = labelX.value + labelWidth.value / 2;
      labelWidth.value = 0;
      labelHeight.value = 0;
    });
  }
});
const hideUnit = ref(false);
watch(hideUnit, (v) => {
  if (v) {
    unitWidth.value = 0;
    unitHeight.value = 0;
  }
});
/**
 * contextmenu
 */
function contextmenu(e: MouseEvent, el: SVGGElement) {
  const arr: ContextmenuItem[] = [
    {
      name: hideBg.value ? "显示背景" : "隐藏背景",
      handler() {
        hideBg.value = !hideBg.value;
      },
    },
  ];
  const toggleValue: ContextmenuItem = {
    name: hideValue.value ? "显示value" : "隐藏value",
    handler() {
      hideValue.value = !hideValue.value;
    },
  };
  const toggleLabel: ContextmenuItem = {
    name: hideLabel.value ? "显示label" : "隐藏label",
    handler() {
      hideLabel.value = !hideLabel.value;
    },
  };
  const toggleUnit: ContextmenuItem = {
    name: hideUnit.value ? "显示unit" : "隐藏unit",
    handler() {
      hideUnit.value = !hideUnit.value;
    },
  };
  if (!hideValue.value && !hideLabel.value) {
    arr.push(toggleLabel, toggleValue, toggleUnit);
  } else if (hideValue.value) {
    arr.push(toggleValue);
  } else if (hideLabel.value) {
    arr.push(toggleLabel, toggleUnit);
  }
  if (!hideValue.value && !hideLabel.value) {
    arr.push({
      name: position.value === "vertical" ? "水平模式" : "垂直模式",
      handler() {
        position.value = position.value === "vertical" ? "horizontal" : "vertical";
      },
    });
  }
  compCommon.showContextmenu(e, {
    data: arr,
    id: props.id,
    element: el,
  });
}
/**
 * rotate
 */
const centerPoint = computed(() => {
  return {
    x: selectX.value + selectWidth.value / 2,
    y: selectY.value + selectHeight.value / 2,
  };
});
const { rotate, rotateStart } = useRotate({
  getRelativeElement() {
    return (compCommon.getSvgInfo() as SvgInfo).container;
  },
  centerPoint() {
    return centerPoint.value;
  },
});
/**
 * split
 */
const splitProps = computed(() => {
  if (position.value === "vertical") {
    return {
      x: labelX.value,
      y: labelY.value + labelHeight.value,
      width: Math.max(labelWidth.value, valueWidth.value),
      height: LabelToValue,
    };
  } else {
    return {
      x: labelX.value + labelWidth.value,
      y: labelY.value,
      width: LabelToValue,
      height: Math.max(labelHeight.value, valueHeight.value),
    };
  }
});

onBeforeUnmount(() => {
  if (selected.value) {
    selected.value = false;
    compCommon.toggleSelect({
      type: ConfigurationType.LabelValue,
      selected: false,
      index: props.index,
    });
  }
});
watch(
  () => props.scaleFactor,
  (s) => {
    labelX.value = _labelX * s;
    labelY.value = _labelY * s;
    labelCssProps.value = Object.assign({}, labelCssProps.value, { "font-size": _labelFontSize * s + "px" });
    valueCssProps.value = Object.assign({}, valueCssProps.value, { "font-size": _valueFontSize * s + "px" });
    unitCssProps.value = Object.assign({}, unitCssProps.value, { "font-size": _unitFontSize * s + "px" });
  }
);

const dataKey = ref<string | null>(null);
watch([rotate, position, hideValue, hideUnit, hideLabel, dataKey], (v) => {
  compCommon.updatePanelInfo({
    type: ConfigurationType.LabelValue,
    panelInfo: {
      rotate: v[0],
      position: v[1],
      hideValue: v[2],
      hideUnit: v[3],
      hideLabel: v[4],
      dataKey: v[5],
    },
  });
});
const animateClass = ref<AnimateClass | "">("");
/**
 * expose
 */
let originalProps: LabelValueOriginalProps | null = null;
const conditionData = ref<Condition[]>([]);
function getOriginalProps() {
  if (!originalProps) {
    return {
      rotate: rotate.value,
      labelCssProps: labelCssProps.value,
      valueCssProps: valueCssProps.value,
      animateClass: animateClass.value,
      unitCssProps: unitCssProps.value,
    };
  }
  return originalProps;
}
function previewCondition(condition: Partial<Condition>) {
  if (!originalProps) {
    originalProps = getOriginalProps();
  }
  condition.rotate !== undefined && (rotate.value = condition.rotate);
  condition.labelCssProps !== undefined && (labelCssProps.value = getLabelCssProps(condition.labelCssProps));
  condition.valueCssProps !== undefined && (valueCssProps.value = getValueCssProps(condition.valueCssProps));
  condition.unitCssProps !== undefined && (unitCssProps.value = getValueCssProps(condition.unitCssProps));
  condition.animateClass !== undefined && (animateClass.value = condition.animateClass);
}
function restoreCondition() {
  if (originalProps) {
    rotate.value = originalProps.rotate;
    labelCssProps.value = getLabelCssProps(originalProps.labelCssProps);
    valueCssProps.value = getValueCssProps(originalProps.valueCssProps);
    unitCssProps.value = getUnitCssProps(originalProps.unitCssProps);
    animateClass.value = originalProps.animateClass;
    originalProps = null;
  }
}
const myExpose: ConfigurationInstance<LabelValueJSON> = {
  isInBoxSelect(boxSelect) {
    const isIn = isRectIntersect(
      { x: labelX.value - SelectToContent, y: labelY.value - SelectToContent },
      selectWidth.value,
      selectHeight.value,
      { x: boxSelect.x, y: boxSelect.y },
      boxSelect.width,
      boxSelect.height
    );
    return isIn;
  },
  select() {
    select(true);
  },
  toJSON() {
    //保存的时候 保存的是 缩放前的数据
    return {
      id: props.id,
      position: position.value,
      labelText: labelText.value,
      valueText: valueText.value,
      unitText: unitText.value,
      rotate: rotate.value,
      dataKey: dataKey.value,
      //保存缩放前的数据
      labelX: _labelX,
      labelY: _labelY,
      labelCssProps: Object.assign({}, labelCssProps.value, { "font-size": _labelFontSize + "px" }),
      valueCssProps: Object.assign({}, valueCssProps.value, { "font-size": _valueFontSize + "px" }),
      unitCssProps: Object.assign({}, unitCssProps.value, { "font-size": _unitFontSize + "px" }),
      hideValue: hideValue.value,
      hideUnit: hideUnit.value,
      hideLabel: hideLabel.value,
      hideBg: hideBg.value,
      conditionData: conditionData.value,
    };
  },
  setJSON(data) {
    position.value = data.position;
    labelText.value = data.labelText;
    valueText.value = isEditor.value ? data.valueText : "--";
    unitText.value = data.unitText;
    rotate.value = data.rotate;
    labelCssProps.value = data.labelCssProps;
    valueCssProps.value = data.valueCssProps;
    unitCssProps.value = data.unitCssProps;
    _labelFontSize = parseInt(data.labelCssProps["font-size"] as string) || TextFontSzie;
    _valueFontSize = parseInt(data.valueCssProps["font-size"] as string) || TextFontSzie;
    _unitFontSize = parseInt((data.unitCssProps || {})["font-size"] as string) || TextFontSzie;
    hideValue.value = data.hideValue || false;
    hideUnit.value = data.hideUnit || false;
    if (data.hideLabel) {
      //当label隐藏后，labelXY不能变，要取JSON的labelXY，所以要阻止hideLabel的watch触发代码
      isTriggerHideLabelWatch = false;
      labelWidth.value = 0;
      labelHeight.value = 0;
    }
    hideLabel.value = data.hideLabel || false;
    hideBg.value = data.hideBg || false;
    dataKey.value = data.dataKey;
    conditionData.value = data.conditionData;
    labelX.value = data.labelX;
    labelY.value = data.labelY;
    nextTick(() => {
      isTriggerHideLabelWatch = true;
    });
  },
  handleData(data) {
    if (dataKey.value) {
      const value = data[dataKey.value];
      if (value !== undefined) {
        valueText.value = value;
      }
    }
    restoreCondition();
    const applyCondition = getApplyCondition(conditionData.value, data);
    if (applyCondition.length > 0) {
      applyCondition.forEach((el) => {
        previewCondition(el);
      });
    }
  },
  getConditionData() {
    return conditionData.value;
  },
  getDataKey() {
    return dataKey.value;
  },
  setConditionData(data) {
    conditionData.value = data;
  },
  previewCondition,
  restoreCondition,
  getDisplayInfo() {
    return {
      name: labelText.value,
    };
  },
  getOriginalProps,
  getBox() {
    return {
      x: labelX.value - SelectToContent,
      y: labelY.value - SelectToContent,
      width: selectWidth.value,
      height: selectHeight.value,
      maxX: labelX.value - SelectToContent + selectWidth.value,
      maxY: labelY.value - SelectToContent + selectHeight.value,
    };
  },
  translate(dx, dy) {
    labelX.value = labelX.value + dx;
    labelY.value = labelY.value + dy;
  },
};
defineExpose(myExpose);
</script>
